home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / protect1.zip / PROTECT.ASM next >
Assembly Source File  |  1992-12-12  |  24KB  |  633 lines

  1. ;;;; Placed into the public domain 12 Dec 1992.
  2.  
  3. ;;;; MS-DOS 8086/8088 assembly language program to load a TSR which
  4. ;;;;  traps disk writes at the BIOS level (INT 13h).
  5.  
  6. ;;; NOTE: The user refers to the drives as 0-6; the are represented
  7. ;;;  internally as 1-7.
  8.  
  9. ;;;; NOTE: This file must be assembled into a .COM file.  See
  10. ;;;;  the associated make files for the command procedures.
  11.  
  12.  
  13. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  14. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  15. ;;
  16. ;;; Define a macro to call the DOS string output function.
  17. ;;;  There is one parameter: the address of the beginning of the string.
  18. ;;;  (Offset only; segment must be in ds).  The string must end in
  19. ;;;  a $ (a prerequisite for the DOS function).
  20. ;;
  21. StringOut MACRO stringAddress
  22.         lea dx,stringAddress ; Load offset of message.
  23.         mov ah,09h           ; DOS function to print a string.
  24.         int 21h              ; Call DOS.
  25.           ENDM
  26.  
  27. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  28. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  29. ;;
  30. ;;; Set up segments and names.  Necessary to produce a .com file
  31. ;;;  with some assemblers.
  32. ;;
  33. Protect SEGMENT
  34. Main    PROC FAR
  35.         ASSUME cs:Protect
  36.         ASSUME ds:Protect
  37.  
  38. ;;
  39. ;;; This assembler directive to start assembled code at address 0100h
  40. ;;;  is necessary when assembling into .COM files.
  41. ;;
  42.         ORG 0100h
  43.  
  44. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  45. ;;
  46. ;;; This is the starting point when this program is entered from DOS
  47. ;;;  as a command.
  48. ;;
  49. go:
  50.         jmp initialize       ; Jump to the initialization function.
  51.  
  52. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  53. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  54. ;;
  55. ;;; STAY RESIDENT CODE: This is the Protect interrupt handler.  The
  56. ;;;  Protect INT 13h (disk access through the BIOS) routine will:
  57. ;;;   1) Check the disk accessed.  If it is a floppy disk or an
  58. ;;;   unprotected hard disk, allow it by calling the DOS INT 13h
  59. ;;;   handler.
  60. ;;;   2) Check the disk function.  If it is not a write or format,
  61. ;;;   allow it.
  62. ;;;   3) If the request is not allowed, set up the registers to
  63. ;;;   show a protected disk error, and return.
  64. ;;
  65. startResidentCode:
  66.  
  67. ;;
  68. ;;; Data storage area for stay resident routine.
  69. ;;
  70. oldVector  DD ?   ; The original vector address.
  71. protStatus DB 0ffh ; The protection status:
  72.                    ;  00h = Protection off.
  73.                    ;  ffh = Protect all hard drives (drives greater than 80h).
  74.                    ;  01h = Protect just the first drive (drive 0)...
  75.                    ;  0xh = Protect just the drives who's bits are on.
  76.                    ;        Example: 0101 protects zero and two.
  77.  
  78. ;;
  79. ;;; The Protect interrupt handler for 13h starts here.
  80. ;;;  If this code is executed, then a disk access request has been
  81. ;;;  made to the BIOS through interrupt 13h.
  82. ;;
  83. protINT13handler:
  84.  
  85. ;;
  86. ;;; Check if this disk is protected.  Disk to be accessed is in register dl.
  87. ;;
  88.         cmp dl,80h      ; Floppy disk request,
  89.         jb allowAccess  ;  allow access.
  90.  
  91.                   ; Note: Only 16 bit quantities may be pushed & popped.
  92.         push cx   ; Save the count register (need for shift).
  93.         push ax   ; Save the accumulator (needed for shift).
  94.  
  95.         mov cl,dl  ; The count register will hold
  96.         sub cl,80h ;  the fixed disk number,
  97.         inc cl     ;  counting from one.
  98.         mov al,80h ; This bit will be shifted into the accumulator,
  99.         rol al,cl  ;  <drive number> times, to match the protStatus
  100.                    ;  bit for this fixed drive.
  101.  
  102.         test al,cs:protStatus ; Load flags with comparison, to be checked
  103.                              ;  below.  This is a one segment routine, so
  104.                              ;  the data segment must be overridden
  105.  
  106.         pop ax  ; Restore registers.  Luckily, the pop instruction does not
  107.         pop cx  ;  affect the flags (which haven't been checked yet).
  108.  
  109.         jz allowAccess ; Check the flags from the above "test".  If the bit
  110.                        ;  for this drive is off, allow the access.
  111.  
  112. ;;
  113. ;;; If here, then the requested disk is protected.  See if the requested
  114. ;;;  service (in register ah) will change data on the disk (is destructive).
  115. ;;
  116.         cmp ah,03h   ; Write sector requested.
  117.         je causeError
  118.         cmp ah,05h   ; Format sector requested.
  119.         je causeError
  120.         cmp ah,06h   ; Format track requested (XT class BIOS only).
  121.         je causeError
  122.         cmp ah,07h   ; Format disk requested (XT class BIOS only).
  123.         je causeError
  124.         cmp ah,0Bh   ; Write long sector requested (DOS diagnostic routine).
  125.         je causeError
  126.  
  127. ;;
  128. ;;; If here, the disk request is not destructive;
  129. ;;;  jump to the original vector.
  130. ;;
  131. allowAccess:
  132.         jmp cs:[oldVector] ; Intersegment jump (assembler knows this because
  133.                            ;  oldVector is type DD).  Note this "data" is
  134.                            ;  in the code segment.
  135.  
  136. ;;
  137. ;;; Abort the disk request.  Return with the registers set to:
  138. ;;;  "write protect error on disk".
  139. ;;
  140. causeError:
  141.         mov ah,03h   ; Return value for interrupt - write protect error.
  142.         stc          ; Set carry flag, to signal error to caller.
  143.         ret 0002h    ; Return from interrupt; iret is not used because
  144.                      ;  it sets the flags back to what they when the
  145.                      ;  interrupt happened (resetting the carry flag);
  146.                      ;  ret 2 throws the old flags away, then returns.
  147.  
  148. ;;
  149. ;;; End of stay resident code.
  150. ;;
  151. endResident:
  152.  
  153. ;;
  154. ;;; The 'magicSize' constant will be used to determine if this
  155. ;;;  interrupt handler is already loaded.  The above routine will be
  156. ;;;  compared with the current handler to see if they are the same.
  157. ;;
  158. magicSize EQU endResident - protINT13handler
  159.  
  160. ;;
  161. ;;; The protection status offset is the distance in bytes from
  162. ;;;  the top of the interrupt handler to the byte containing the
  163. ;;;  protection status.   Useful for for finding the protection status
  164. ;;;  of the resident copy of protect from a second copy.  (See below.)
  165. ;;
  166. protStatusOffset EQU protINT13handler - protStatus
  167.  
  168.  
  169. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  170. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  171. ;;
  172. ;;; THIS CODE IS ALWAYS DISCARDED (even if exit is TSR).
  173. ;;
  174.  
  175. ;;
  176. ;;; This message will be embedded in the code.
  177. ;;
  178. copyrightMes DB 'Binary and source in the public domain.'
  179.  
  180. ;;
  181. ;;; Storage space for discarded code.
  182. ;;
  183. thereIsParam DB  00h  ; Are there parameters?
  184.  
  185. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  186. ;;
  187. ;;; INITIALIZATION CODE.  Check parameters; then check if Protect
  188. ;;;  interrupt handler is already loaded.
  189. ;;
  190. initialize:
  191.  
  192. IFDEF MAKING_PROTECT ; ### Only if making protect.com file.
  193.  
  194. ;;
  195. ;;; SET UP SEGMENT REGISTERS: When this program is entered from DOS,
  196. ;;;  the ds (data segment) register points to the beginning of the
  197. ;;;  PSP (Program Segment Prefix); and the cs (code segment) register
  198. ;;;  points to the "go:" label, above.
  199. ;;; This is one segment program; the code and data are both in
  200. ;;;  the code segment.  So, after saving the ds register (the PSP
  201. ;;;  will be needed to find any parameters), make the ds register
  202. ;;;  the same as the cs register.
  203. ;;
  204.         mov ax,ds  ; Save the ds register (now at the beginning of the PSP),
  205.         mov es,ax  ;  into the es register (PSP is needed by FindParam).
  206.                    ; (Two moves are needed because of addressing
  207.                    ;   restrictions.)
  208.  
  209.         mov ax,cs  ; Load data segment register.  (Two move because
  210.         mov ds,ax  ;  of addressing restrictions.)
  211.  
  212. ;;
  213. ;;; FIND AND CHECK THE PARAMETERS: Parameters are used to protect some
  214. ;;;  disks while leaving others unprotected.  The parameter must be
  215. ;;;  an integer between 0 and 6, or A to protect all drives.
  216. ;;;  If there is a parameter, protection will be added for that physical
  217. ;;;  drive (or all drives).
  218. ;;
  219.         call FindParam ; Call subroutine to parse parameter list.
  220.                        ;  Results will be returned in the al and
  221.                        ;  bl registers.
  222.  
  223.         cmp al,02h     ; Check for invalid parameters.
  224.         jne paramsOK   ; Jump if parameters are ok.
  225.  
  226.         StringOut invalidParamMes ; Bad parameters: show usage message, -
  227.         jmp exitOut               ;  then exit.
  228.  
  229. paramsOK:
  230.         cmp al,00h    ; There are no parameters;
  231.         je doneParam  ;  there is no need to do anything;
  232.                       ;  the default is no parameters.
  233.  
  234. ;;
  235. ;;; If here, there is a valid parameter. Save it, then check to
  236. ;;;  see how many drives there are.
  237. ;;
  238.         mov thereIsParam,0ffh ; Record presence of parameter.
  239.         cmp bl,0ffh         ; Test for protect all drives.
  240.         jne setStatus       ; If not, set the status bits for the drive, etc..
  241.         mov protStatus,0ffh ; Protect all drives.
  242.         jmp doneParam       ; Jump over set status & check drive number.
  243.  
  244. setStatus:
  245.         mov cl,bl  ; Load count register with drive number.
  246.                    ; Make protection status bit for this drive.
  247.                    ;  (Drive number is now in the cl register.)
  248.         mov al,80h ; This bit will be shifted into al,
  249.         rol al,cl  ;  <drive number> times, to match the protStatus
  250.                    ;  bit for this fixed drive.
  251.  
  252.         mov protStatus,al ; Save the drive number as protection status.
  253.  
  254.         mov ah,08h  ; Get ready to use fixed drive function
  255.         mov dl,80h  ;  to get information (08h); 80h specifies
  256.                     ;  fixed disk information.
  257.         int 13h     ; Call BIOS - 13h is fixed drive request.
  258.                     ; The number of fixed disks is in register dl.
  259.  
  260.         cmp bl,dl     ; Compare requested fixed disk with
  261.                       ;  the actual number of fixed disks.
  262.         jbe doneParam ; The requested drive does exist.
  263.  
  264.                    ; If here, the requested drive does not exist.
  265.         mov cl,dl  ; Save number of drives.
  266.         StringOut tooManyMes ; Show message '...last drive is'
  267.  
  268.         dec cl      ; Drive are numbered 0-6 to the user;
  269.                     ;  and numbered 1-7 internally.
  270.         add cl,'0'  ; Make drive number into ascii.
  271.         mov dl,cl   ; DOS function to output a single character, -
  272.         mov ah,02h  ;  (in the dl register).
  273.         int 21h     ; Call DOS.
  274.         jmp exitOut ; Exit.
  275.  
  276. ENDIF ; ### End of protect specific code.
  277.  
  278. ;;
  279. ;;; USE DOS TO GET THE OLD VECTOR (address of handler) for interrupt 13h.
  280. ;;;  The vector is needed both to test if the Protect handler is already
  281. ;;;  there and to be able to call the old handler, for allowed disk
  282. ;;;  services (example: reads).
  283. ;;
  284. doneParam:
  285.         mov ax,3513h   ; DOS function to get vector 13h.
  286.         int 21h        ; Call DOS.
  287.  
  288. ;;
  289. ;;; Move the vector for the DOS interrupt 13h handler into storage
  290. ;;;  space.  The space is in the stay resident section, so the
  291. ;;;  DOS handler can be called from the Protect handler.
  292. ;;
  293.         mov word ptr oldVector,bx    ; Store offset first,
  294.         mov word ptr oldVector[2],es ;  then segment.  Order is important,
  295.                                      ;  to be able to do a jump far later.
  296.  
  297. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  298. ;;
  299. ;;; TEST IF NEW HANDLER HAS ALREADY BEEN INSTALLED.  See
  300. ;;;  note with label magicSize, above.  The built in string
  301. ;;;  comparison instructions are used.
  302. ;;
  303.         mov di,bx ; Load destination string address offset register; -
  304.                   ;  (No need to load destination segment register,
  305.                   ;  it is es, which was is already loaded, from above.)
  306.         lea si,protINT13handler ; Load source string offset register;
  307.                                 ;  (segment is ds).
  308.         mov cx,magicSize ; Load string length.
  309.         cld ; Clear direction flag: string operation will autoincrement.
  310.         repe cmpsb   ; Repeat string comparison while strings are equal.
  311.         jz alreadyLoaded ; If the end of the string is reached with all
  312.                          ;  elements equal, this handler has already been
  313.                          ;  loaded.
  314.  
  315. IFDEF MAKING_UNPROT ; ### Only if making protect.
  316.  
  317.         StringOut protNotOnMes
  318.         jmp exitOut
  319.  
  320. ELSE ; ### MAKING_PROTECT
  321.  
  322. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  323. ;;
  324. ;;; LOAD NEW INTERRUPT (protection): Use DOS to set the Protect vector
  325. ;;;  (address of handler) for interrupt 13h.  The Protect handler is
  326. ;;;  in the stay resident code.
  327. ;;
  328.         lea dx,protINT13handler  ; Load vector offset for Protect
  329.                                  ;  interrupt 13 handler into dx.
  330.         mov ax,2513h  ; DOS function to set vector 13h.
  331.         int 21h       ; Call DOS.
  332.  
  333. ;;
  334. ;;; Show protection loaded message and list protected disks.
  335. ;;
  336.         StringOut loadingMes ; Print protection on message.
  337.         mov bh,protStatus    ; Get ready to list protected disks.
  338.         call ReportProt      ; Call function to list protected disks.
  339.  
  340. ;;
  341. ;;; Terminate and stay resident.  The DOS function will want to know the
  342. ;;;  first available location after resident code in *paragraphs*.
  343. ;;; There are 16 bytes to a paragraph.  Right shifting 4 times will
  344. ;;;  divide the byte count generated by the assembler into paragraphs.
  345. ;;
  346.         lea dx,endResident ; Load end of resident byte offset.
  347.         mov cx,0004h       ; Prepare CS:CL (count) register for right shift 4.
  348.         shr dx,cl  ; Compute number of paragraphs by division.
  349.         inc dx     ; Increment number of paragraphs, to take care of round off.
  350.         mov ax,3100h  ; DOS function to terminate but stay resident.
  351.         int 21h    ; Call DOS.
  352.  
  353. ENDIF ; ### Of If making Protect.
  354.  
  355. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  356. ;;
  357. ;;; THE PROTECT HANDLER IS ALREADY LOADED.  If there are no parameters,
  358. ;;;  report the protected disks, then exit.  If there are parameters,
  359. ;;;  (a specific disk) added the requested disk to the protected list.
  360. ;;;  For either function, the current protection status is needed.
  361. ;;; Remember: at this point there are two copies of the PROTECT interrupt
  362. ;;;  13h handler in memory; the one that was there (loaded previously,
  363. ;;;  and just found), and a second one loaded by running protect
  364. ;;;  again.  The second copy will be ignored; it is redundant (and
  365. ;;;  its storage will be given back when the second copy of protect
  366. ;;;  does a normal exit).  When the protection status is checked or
  367. ;;;  changed, the *old* copy of the interrupt handler must be used.
  368. ;;
  369. alreadyLoaded:
  370.         StringOut wasHereMes ; Show protection was loaded message.
  371.  
  372. IFDEF MAKING_UNPROT ; ### Turn protection off.
  373.  
  374.         mov byte ptr es:[bx-protStatusOffset],00h ; Turn off protection.
  375.         mov al,00h
  376.  
  377. ELSE ; ### MAKING_PROTECT
  378.  
  379.         mov al,es:[bx-protStatusOffset]  ; Get the protection status from
  380.                                          ;  the copy of protect being used.
  381.  
  382.         cmp thereIsParam,00h  ; If there are no parameters,
  383.         je reportFromAlready  ;  just report, then exit.
  384.  
  385.         mov cl,protStatus ; Disk to be added to protection status,
  386.                           ;  (was stored in *new* copy of PROTECT).
  387.         or al,cl          ; Add disk to the list of protected disks.
  388.         mov es:[bx-protStatusOffset],al ; Put the new protection status
  389.                                         ;  back into the copy of Protect
  390.                                         ;  being used.
  391.  
  392. ENDIF ; ### 
  393.  
  394. reportFromAlready:
  395.         mov bh,al       ; Move protection status into bh.
  396.         call ReportProt ; Call function to list protected disks.
  397.  
  398. exitOut:
  399.         mov ax,4c00h  ; DOS function for normal exit.
  400.         int 21h       ; Call DOS.
  401.  
  402. Main    ENDP
  403.  
  404.  
  405.  
  406. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  407. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  408. ;;
  409. ReportProt PROC NEAR
  410. ;;
  411. ;;; SUBROUTINE TO REPORT PROTECTION STATUS:
  412. ;;; This subroutine will expect the bh register to contain the
  413. ;;;  the current protection status.
  414. ;;; Nothing is returned.
  415. ;;; No attempt is made to preserve register contents, except for
  416. ;;;  segment registers.
  417. ;;
  418.         StringOut listHeaderMes ; Announce list.
  419.  
  420.         cmp bh,00h       ; Test if protection has been turned off;
  421.         je noneProtected ;  if so, jump to none protected message.
  422.  
  423.         cmp bh,0ffh;    ; Test if all fixed disks are protected;
  424.         je allProtected ;  if so, jump to all protected message.
  425.  
  426. ;;
  427. ;;; Check each potential disk.  DOS only allows 2; but for future
  428. ;;;  expansion, this program allows up to 7, if they exist.
  429. ;;;  The existence of a fixed disk is not checked here.  Since disks
  430. ;;;  are checked before being protected, it is assumed that if a disk
  431. ;;;  is protected, it exists.
  432. ;;
  433.         sub cx,cx  ; Clear the count register.
  434.         mov cl,07h ; Count down 7 drives (6-0).
  435. topOfCheckLoop:
  436.         mov bl,80h ; This bit will be shifted into bl,
  437.         rol bl,cl  ;  <drive number> times, to match the protStatus
  438.                    ;  bit for this fixed drive.
  439.  
  440.         test bh,bl   ; If this disk is not protected,
  441.         jz loopAgain ;  the ZF flag is now set.
  442.  
  443.         mov dl,cl   ; This disk is protected,
  444.         add dl,'0'  ;  output as an ascii number,
  445.         dec dl      ;   in (0-6).
  446.         mov ah,02h  ; DOS function to output a single character.
  447.         int 21h     ; Call DOS.
  448.  
  449.         mov dl,' '  ; Output a trailing space,
  450.         int 21h     ;  to be ready for the next disk.
  451.  
  452. loopAgain:
  453.         loop topOfCheckLoop  ; Single instruction to decrement cl,
  454.                              ;  test it for not zero, and if so, go
  455.                              ;  back to the top of the loop.
  456.  
  457.         jmp retRProt ; Jump over below messages to the return statement.
  458.  
  459. allProtected:
  460.         StringOut allProtMes ; Show all disks protected message.
  461.         jmp retRProt         ; Jump to the return.
  462.  
  463. noneProtected:
  464.         StringOut noneProtMes ; Show no disks protected message.
  465.  
  466. ;;
  467. ;;; Return from subroutine Report Protection.
  468. ;;
  469. retRProt:
  470.         ret
  471.  
  472. ReportProt ENDP
  473.  
  474.  
  475. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  476. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  477. ;;
  478.  
  479. IFDEF MAKING_PROTECT ; ### Only called by protect, not unprot
  480.  
  481. FindParam PROC NEAR
  482. ;;
  483. ;;; SUBROUTINE TO FIND THE PARAMETERS, if any.  The only acceptable
  484. ;;;  parameter list is white space (spaces and tabs), followed by
  485. ;;;  an integer between 0 and 6; or A to protect all drives.  
  486. ;;;  If there is a parameter, protection will be added for that disk.
  487. ;;;
  488. ;;; NOTE: The user refers to the drives as 0-6; they are represented
  489. ;;;  internally as 1-7.
  490. ;;;
  491. ;;; This subroutine will expect: The cs & ds registers both point to
  492. ;;;  the beginning of the segment this code is in; and the
  493. ;;;  es register points to the beginning of the PSP.
  494. ;;; No attempt is made to preserve register contents, except for
  495. ;;;  segment registers.
  496. ;;; The al & bl registers will contain the results: if al is 0, there
  497. ;;;  are no parameters.  If al is 1, there was a parameter (in bl); if
  498. ;;;  al is 2, the parameters were invalid.  (bl will contain ff if 
  499. ;;;  parameter was A.)
  500. ;;;
  501. ;;; A string of white space with nothing following is recognized as no
  502. ;;;  parameters; also trailing white space after a valid parameter
  503. ;;;  is ignored.
  504. ;;
  505.         sub cx,cx  ; Clear count register.
  506.  
  507.         mov bx,0080h    ; Load length (in bytes) of the parameter string
  508.         mov cl,es:[bx]  ;  into the string count register.
  509.         mov bx,cx       ; Save the count, for later use.
  510.  
  511.         cmp cl,00h ; If the length of the parameter string is 0,
  512.         je noParm  ;  there are no parameters.
  513.  
  514. ;;
  515. ;;; Convert tabs to spaces in the parameter string.
  516. ;;
  517.         mov di,0081h ; Load the string function offset register to
  518.                      ;  be the beginning of the parameters.
  519.         mov al,09h   ; Character to search for (tab).
  520.         cld          ; Clear direction flag, so string operation will increment.
  521.  
  522. convertTabsLoop:
  523.         repne scasb    ; Scan to the first non-tab, or end of string.
  524.         jne scanSpaces ; Last character wasn't tab, end of string was found.
  525.  
  526.         mov byte ptr es:[di-1],' ' ; Change the tab to a space.
  527.  
  528.         cmp cx,0000h        ; If there are more characters in the string,
  529.         jne convertTabsLoop ;  then keep looking.
  530.  
  531. ;;
  532. ;;; Scan over white space to find parameter.
  533. ;;
  534. scanSpaces:
  535.         mov cx,bx     ; Restore the string length (count).
  536.         mov di,0081h  ; Restore the beginning of string pointer.
  537.  
  538.         mov al,' '   ; Character to search for (space).
  539.         cld          ; Make sure direction flag is clear (see above).
  540.         repe scasb   ; Scan to the first non-space.
  541.  
  542.         je noParm   ; If last character was space, no parameters.
  543.  
  544.         mov bl,es:[di-1] ; Save the first non-space in bl; if the parameters
  545.                          ;  are okay, it is the drive number.
  546.  
  547.         cmp cx,0000h ; If there no characters left, everything is okay,
  548.         je validList ;  otherwise, look for trailing white space.
  549.  
  550.         repe scasb       ; Scan over trailing characters. If any aren't
  551.         jne invalidParam ;  white space, there is an error in the parameters.
  552.  
  553. ;;
  554. ;;; If here, then the list is: <white space><single character>[<white space>]
  555. ;;;  Now verify the character is an integer between 0 and 6; or A.
  556. ;;
  557. validList:
  558.         cmp bl,'A'  ; Check for capital A.
  559.         je paramIsA
  560.         cmp bl,'a'  ; Check for small a.
  561.         je paramIsA
  562.  
  563. ;;
  564. ;;; If here, the paramater is not an A.
  565. ;;
  566.         sub bl,'0'       ; Convert from ASCII to an integer.
  567.         jb invalidParam  ; If below zero, there is an error.
  568.         cmp bl,06h       ; Test for parameter too big.
  569.         ja invalidParam  ; Parameter is too big.
  570.  
  571. ;;
  572. ;;; If here, the parameter is a valid number.
  573. ;;
  574.         inc bl ; The user refers to the drives as 0-6,
  575.                ;  but they are represented internally as 1-7.
  576.  
  577.         mov al,01h    ; Tell caller there are parameters,
  578.         jmp retFParam ;  then return.
  579.  
  580. ;;
  581. ;;; Set up return values to protect all drives.
  582. ;;
  583. paramIsA:
  584.         mov bl,0ffh   ; Tell caller the parameter was A.
  585.         mov al,01h    ; Tell caller there are parameters, 
  586.         jmp retFParam ;  then return.
  587.  
  588. ;;
  589. ;;; If here, there is a parameter list, but it invalid.
  590. ;;
  591. invalidParam:
  592.         mov al,02h    ; Tell caller parameters are invalid.
  593.         jmp retFParam  ; Return.
  594.  
  595. ;;
  596. ;;; If here, there are no parameters.
  597. ;;
  598. noParm:
  599.         sub al,al  ; Clear the al register (no parameters signal).
  600.  
  601. ;;
  602. ;;; Return from subroutine "Find the parameters".
  603. ;;;
  604. retFParam:
  605.         ret
  606.  
  607. FindParam ENDP
  608.  
  609. ENDIF ; ### End of if making protect.
  610.  
  611. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  612. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  613. ;;
  614. ;;; MESSAGE STRING STORAGE AREA:
  615. ;;;  "$" marks end of string to the DOS string display function.
  616. ;;
  617. invalidParamMes  DB 'Usage: protect [0-6 | a]$'
  618. tooManyMes       DB 'Drive number too big; last drive is: $'
  619. loadingMes       DB 'Loading Protection: $'
  620. listHeaderMes    DB 'List of protected physical disks: $'
  621. wasHereMes       DB 'Protection already loaded: $'
  622. allProtMes       DB 'All.$'
  623. noneProtMes      DB 'None.$'
  624. protNotOnMes     DB 'Protect not loaded.$' 
  625.  
  626. ;;
  627. ;;; Trailing information.  Necessary to produce a .com file with
  628. ;;;  some assemblers.
  629. ;;
  630. Protect ENDS
  631.         END go
  632.  
  633.